package com.xyz.service.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xyz.entity.mapper.*;
import com.xyz.entity.pojo.*;
import com.xyz.service.CommonService;
import com.xyz.service.UserService;
import com.xyz.util.HmacSHA256Util;
import com.xyz.util.RedisUtil;
import com.xyz.util.dto.DataResult;
import com.xyz.util.dto.GridData;
import com.xyz.util.dto.LoginInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {

    @Autowired
    private CommonService commonService;

    @Autowired
    private SystemUserMapper systemUserMapper;

    @Autowired
    private SystemEmployeeMapper systemEmployeeMapper;

    @Autowired
    private SystemRoleMapper systemRoleMapper;

    @Autowired
    private SystemUrlLogMapper systemUrlLogMapper;

    @Autowired
    private SystemRoleDirectoryMapper systemRoleDirectoryMapper;

    @Autowired
    private SystemDirectoryMapper systemDirectoryMapper;

    @Autowired
    private RedisUtil redis;

    HashMap<String, String> lessLoginSecretMap = MapUtil.of("12bacf", "58b40dcb4ab34376b6c2d7785db071cf");

    @Override
    public DataResult passwordLessLogin(String username, String clientId, String ts, String signature) throws Exception {
        String sb = "username=" + username + "&clientId=" + clientId + "&ts=" + ts;
        String secret = lessLoginSecretMap.get(clientId);
        if (StrUtil.isBlank(secret)) {
            return DataResult.build9250("参数非法");
        }
        String sha256 = HmacSHA256Util.hmacSHA256(secret, sb);
        if (!sha256.equals(signature)) {
            return DataResult.build9250("签名未通过");
        }

        Date requestDate = new Date();
        SystemUserExample suExample = new SystemUserExample();
        suExample.createCriteria().andUsernameEqualTo(username).andIsDeleteEqualTo(0);
        List<SystemUser> suList = systemUserMapper.selectByExample(suExample);
        if (suList.size() != 1) {
            return DataResult.build9250("账号未注册");
        }
        SystemUser su = suList.get(0);

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        SystemUrlLog sul = new SystemUrlLog();
        sul.setUserId(su.getId());
        sul.setUsername(su.getUsername());
        sul.setIp(StrUtil.isNotBlank(request.getRemoteAddr()) ? request.getRemoteAddr() : "未知");
        sul.setUrl("/system/passwordLessLogin");
        sul.setRequestTime(requestDate);

        SystemEmployee se = systemEmployeeMapper.selectByPrimaryKey(su.getEmployeeId());
        if (se == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定员工，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }
        SystemRole sr = systemRoleMapper.selectByPrimaryKey(su.getRoleId());
        if (sr == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定角色，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }

        // 挤掉线
        // String oldToken = mixedEncryption(su.getId() + "" + su.getLastLoginTime().getTime());
        // redis.delete(oldToken);

        SystemUser newSu = new SystemUser();
        newSu.setId(su.getId());
        newSu.setLoginTime(new Date());
        newSu.setLastLoginTime(su.getLoginTime());
        systemUserMapper.updateByPrimaryKeySelective(newSu);

        String token = mixedEncryption(su.getId() + "" + newSu.getLoginTime().getTime());
        LoginInfo newLi = new LoginInfo();
        newLi.setLoginUserId(su.getId());
        newLi.setLoginUsername(su.getUsername());
        newLi.setLoginToken(token);
        newLi.setLoginEmployeeId(se.getId());
        newLi.setLoginEmployeeName(se.getEmployeeName());
        newLi.setLoginPhoneNumber(se.getPhoneNumber());
        newLi.setLoginRoleId(sr.getId());
        newLi.setLoginRoleName(sr.getRoleName());
        newLi.setLoginTime(newSu.getLoginTime());
        newLi.setLastLoginTime(newSu.getLastLoginTime());
        String jsonStr = JSONUtil.toJsonStr(newLi);
        redis.set(token, jsonStr, 30, TimeUnit.DAYS);

        sul.setRequestResult(token);
        sul.setResponseTime(new Date());
        systemUrlLogMapper.insert(sul);

        return DataResult.build9100(token);
    }

    @Override
    public DataResult login(String username, String password) throws Exception {
        Date requestDate = new Date();
        SystemUserExample suExample = new SystemUserExample();
        suExample.createCriteria().andUsernameEqualTo(username).andIsDeleteEqualTo(0);
        List<SystemUser> suList = systemUserMapper.selectByExample(suExample);
        if (suList.size() != 1) {
            return DataResult.build9250("账号未注册或密码错误");
        }
        SystemUser su = suList.get(0);

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        SystemUrlLog sul = new SystemUrlLog();
        sul.setUserId(su.getId());
        sul.setUsername(su.getUsername());
        sul.setIp(StrUtil.isNotBlank(request.getRemoteAddr()) ? request.getRemoteAddr() : "未知");
        sul.setUrl("/system/login");
        sul.setRequestTime(requestDate);

        if (!password.equals(su.getPassword())) {
            DataResult dataResult = DataResult.build9250("账号未注册或密码错误");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }

        SystemEmployee se = systemEmployeeMapper.selectByPrimaryKey(su.getEmployeeId());
        if (se == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定员工，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }
        SystemRole sr = systemRoleMapper.selectByPrimaryKey(su.getRoleId());
        if (sr == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定角色，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }

        // 挤掉线
        // String oldToken = mixedEncryption(su.getId() + "" + su.getLastLoginTime().getTime());
        // redis.delete(oldToken);

        SystemUser newSu = new SystemUser();
        newSu.setId(su.getId());
        newSu.setLoginTime(new Date());
        newSu.setLastLoginTime(su.getLoginTime());
        systemUserMapper.updateByPrimaryKeySelective(newSu);

        List<String> loginRoleDirectoryMarkList = new ArrayList<>();
        SystemRoleDirectoryExample srdExample = new SystemRoleDirectoryExample();
        SystemRoleDirectoryExample.Criteria srdCriteria = srdExample.createCriteria();
        srdCriteria.andRoleIdEqualTo(sr.getId());
        List<SystemRoleDirectory> srdList = systemRoleDirectoryMapper.selectByExample(srdExample);
        List<Long> dIdList = CollStreamUtil.toList(srdList, SystemRoleDirectory::getDirectoryId);
        if (dIdList.size() > 0) {
            SystemDirectoryExample sdExample = new SystemDirectoryExample();
            SystemDirectoryExample.Criteria sdCriteria = sdExample.createCriteria();
            sdCriteria.andIdIn(dIdList);
            sdCriteria.andTypeEqualTo(2);
            List<SystemDirectory> sdList = systemDirectoryMapper.selectByExample(sdExample);
            for (SystemDirectory sd : sdList) {
                if (StrUtil.isNotBlank(sd.getMark())) {
                    loginRoleDirectoryMarkList.add(sd.getMark());
                }
            }
        }

        String token = mixedEncryption(su.getId() + "" + newSu.getLoginTime().getTime());
        LoginInfo newLi = new LoginInfo();
        newLi.setLoginUserId(su.getId());
        newLi.setLoginUsername(su.getUsername());
        newLi.setLoginToken(token);
        newLi.setLoginEmployeeId(se.getId());
        newLi.setLoginEmployeeName(se.getEmployeeName());
        newLi.setLoginPhoneNumber(se.getPhoneNumber());
        newLi.setLoginRoleId(sr.getId());
        newLi.setLoginRoleName(sr.getRoleName());
        newLi.setLoginTime(newSu.getLoginTime());
        newLi.setLastLoginTime(newSu.getLastLoginTime());
        newLi.setLoginRoleDirectoryMarkList(loginRoleDirectoryMarkList);
        String jsonStr = JSONUtil.toJsonStr(newLi);
        redis.set(token, jsonStr, 30, TimeUnit.DAYS);

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("directory", commonService.getDirectory(0, su.getRoleId()));

        DataResult dataResult = DataResult.build9100(map);
        sul.setRequestResult(dataResult.toInfo());
        sul.setResponseTime(new Date());
        systemUrlLogMapper.insert(sul);

        return dataResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DataResult appLogin(String username, String password) throws Exception {
        Date requestDate = new Date();
        SystemUserExample suExample = new SystemUserExample();
        suExample.createCriteria().andUsernameEqualTo(username).andIsDeleteEqualTo(0);
        List<SystemUser> suList = systemUserMapper.selectByExample(suExample);
        if (suList.size() != 1) {
            return DataResult.build9250("账号未注册或密码错误");
        }
        SystemUser su = suList.get(0);

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        SystemUrlLog sul = new SystemUrlLog();
        sul.setUserId(su.getId());
        sul.setUsername(su.getUsername());
        sul.setIp(StrUtil.isNotBlank(request.getRemoteAddr()) ? request.getRemoteAddr() : "未知");
        sul.setUrl("/system/login");
        sul.setRequestTime(requestDate);

        if (!password.equals(su.getPassword())) {
            DataResult dataResult = DataResult.build9250("账号未注册或密码错误");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }
        SystemEmployee se = systemEmployeeMapper.selectByPrimaryKey(su.getEmployeeId());
        if (se == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定员工，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }
        SystemRole sr = systemRoleMapper.selectByPrimaryKey(su.getRoleId());
        if (sr == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定角色，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }

        // 挤掉线
        // String oldToken = mixedEncryption(su.getId() + "" + su.getLastLoginTime().getTime());
        // redis.delete(oldToken);

        SystemUser newSu = new SystemUser();
        newSu.setId(su.getId());
        newSu.setLoginTime(new Date());
        newSu.setLastLoginTime(su.getLoginTime());
        systemUserMapper.updateByPrimaryKeySelective(newSu);

        String token = mixedEncryption(su.getId() + "" + newSu.getLoginTime().getTime());
        LoginInfo newLi = new LoginInfo();
        newLi.setLoginUserId(su.getId());
        newLi.setLoginUsername(su.getUsername());
        newLi.setLoginToken(token);
        newLi.setLoginEmployeeId(se.getId());
        newLi.setLoginEmployeeName(se.getEmployeeName());
        newLi.setLoginPhoneNumber(se.getPhoneNumber());
        newLi.setLoginRoleId(sr.getId());
        newLi.setLoginRoleName(sr.getRoleName());
        newLi.setLoginTime(newSu.getLoginTime());
        newLi.setLastLoginTime(newSu.getLastLoginTime());
        String jsonStr = JSONUtil.toJsonStr(newLi);
        redis.set(token, jsonStr, 30, TimeUnit.DAYS);

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("directory", commonService.getDirectory(2, su.getRoleId()));

        DataResult dataResult = DataResult.build9100(map);
        sul.setRequestResult(dataResult.toInfo());
        sul.setResponseTime(new Date());
        systemUrlLogMapper.insert(sul);

        return dataResult;
    }


    @Override
    public DataResult WeChatLogin(String phoneNumber, String password) throws Exception {
        SystemEmployeeExample example = new SystemEmployeeExample();
        example.createCriteria().andPhoneNumberEqualTo(phoneNumber);
        example.setOrderByClause("create_time desc limit 1");
        List<SystemEmployee> systemEmployees = systemEmployeeMapper.selectByExample(example);
        if(CollUtil.isEmpty(systemEmployees)){
            return DataResult.build9250("手机号码未注册");
        }
        SystemEmployee se = systemEmployees.get(0);
        Date requestDate = new Date();
        SystemUserExample suExample = new SystemUserExample();
        suExample.createCriteria().andEmployeeIdEqualTo(se.getId()).andIsDeleteEqualTo(0);
        List<SystemUser> suList = systemUserMapper.selectByExample(suExample);
        if (CollUtil.isEmpty(suList)) {
            return DataResult.build9250("手机号码找不到对应账号");
        }
        SystemUser su = suList.get(0);

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        SystemUrlLog sul = new SystemUrlLog();
        sul.setUserId(su.getId());
        sul.setUsername(su.getUsername());
        sul.setIp(StrUtil.isNotBlank(request.getRemoteAddr()) ? request.getRemoteAddr() : "未知");
        sul.setUrl("/system/WeChatLogin");
        sul.setRequestTime(requestDate);

        if (!password.equals(su.getPassword())) {
            DataResult dataResult = DataResult.build9250("账号未注册或密码错误");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }

        SystemRole sr = systemRoleMapper.selectByPrimaryKey(su.getRoleId());
        if (sr == null) {
            DataResult dataResult = DataResult.build9250("账号未绑定角色，无法登陆");
            sul.setRequestResult(dataResult.toInfo());
            sul.setResponseTime(new Date());
            systemUrlLogMapper.insert(sul);
            return dataResult;
        }

        // 挤掉线
        // String oldToken = mixedEncryption(su.getId() + "" + su.getLastLoginTime().getTime());
        // redis.delete(oldToken);

        SystemUser newSu = new SystemUser();
        newSu.setId(su.getId());
        newSu.setLoginTime(new Date());
        newSu.setLastLoginTime(su.getLoginTime());
        systemUserMapper.updateByPrimaryKeySelective(newSu);

        List<String> loginRoleDirectoryMarkList = new ArrayList<>();
        SystemRoleDirectoryExample srdExample = new SystemRoleDirectoryExample();
        SystemRoleDirectoryExample.Criteria srdCriteria = srdExample.createCriteria();
        srdCriteria.andRoleIdEqualTo(sr.getId());
        List<SystemRoleDirectory> srdList = systemRoleDirectoryMapper.selectByExample(srdExample);
        List<Long> dIdList = CollStreamUtil.toList(srdList, SystemRoleDirectory::getDirectoryId);
        if (dIdList.size() > 0) {
            SystemDirectoryExample sdExample = new SystemDirectoryExample();
            SystemDirectoryExample.Criteria sdCriteria = sdExample.createCriteria();
            sdCriteria.andIdIn(dIdList);
            sdCriteria.andTypeEqualTo(2);
            List<SystemDirectory> sdList = systemDirectoryMapper.selectByExample(sdExample);
            for (SystemDirectory sd : sdList) {
                if (StrUtil.isNotBlank(sd.getMark())) {
                    loginRoleDirectoryMarkList.add(sd.getMark());
                }
            }
        }

        String token = mixedEncryption(su.getId() + "" + newSu.getLoginTime().getTime());
        LoginInfo newLi = new LoginInfo();
        newLi.setLoginUserId(su.getId());
        newLi.setLoginUsername(su.getUsername());
        newLi.setLoginToken(token);
        newLi.setLoginEmployeeId(se.getId());
        newLi.setLoginEmployeeName(se.getEmployeeName());
        newLi.setLoginPhoneNumber(phoneNumber);
        newLi.setLoginRoleId(sr.getId());
        newLi.setLoginRoleName(sr.getRoleName());
        newLi.setLoginTime(newSu.getLoginTime());
        newLi.setLastLoginTime(newSu.getLastLoginTime());
        newLi.setLoginRoleDirectoryMarkList(loginRoleDirectoryMarkList);
        String jsonStr = JSONUtil.toJsonStr(newLi);
        redis.set(token, jsonStr, 30, TimeUnit.DAYS);

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        Map<String, Object> employeeMap = new HashMap<>();
        employeeMap.put("employeeName",se.getEmployeeName());
        employeeMap.put("phoneNumber",se.getPhoneNumber());
        employeeMap.put("position",se.getPosition());
        employeeMap.put("headFileCode",se.getHeadFileCode());
        employeeMap.put("createTime", DateUtil.format(se.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
        map.put("employeeInfo", employeeMap);

        DataResult dataResult = DataResult.build9100(map);
        sul.setRequestResult(dataResult.toInfo());
        sul.setResponseTime(new Date());
        systemUrlLogMapper.insert(sul);

        return dataResult;
    }

    @Override
    public DataResult logout(LoginInfo li) throws Exception {
        redis.delete(li.getLoginToken());
        return DataResult.build9100();
    }

    @Override
    public DataResult selectUser(LoginInfo li, int pageNum, int pageSize, String username, String employeeName) throws Exception {
        SystemRoleExample srExample = new SystemRoleExample();
        srExample.createCriteria().andIsDeleteEqualTo(0);
        List<SystemRole> srList = systemRoleMapper.selectByExample(srExample);
        Map<Long, String> srMap = new HashMap<>();
        for (SystemRole sr : srList) {
            srMap.put(sr.getId(), sr.getRoleName());
        }

        SystemEmployeeExample seExample = new SystemEmployeeExample();
        seExample.createCriteria().andIsDeleteEqualTo(0);
        List<SystemEmployee> seList1 = systemEmployeeMapper.selectByExample(seExample);
        Map<Long, String> seMap = new HashMap<>();
        for (SystemEmployee se : seList1) {
            seMap.put(se.getId(), se.getEmployeeName());
        }

        seExample.clear();
        seExample.createCriteria().andIsDeleteEqualTo(0).andEmployeeNameLike("%" + employeeName + "%");
        List<SystemEmployee> seList2 = systemEmployeeMapper.selectByExample(seExample);
        List<Long> seIdList = new ArrayList<>();
        for (SystemEmployee se : seList2) {
            seIdList.add(se.getId());
        }

        SystemUserExample suExample = new SystemUserExample();
        SystemUserExample.Criteria suCriteria = suExample.createCriteria();
        if (StrUtil.isNotBlank(username)) {
            suCriteria.andUsernameLike("%" + username + "%");
        }
        if (seIdList.size() > 0) {
            suCriteria.andEmployeeIdIn(seIdList);
        }
        suCriteria.andIsDeleteEqualTo(0);
        suExample.setOrderByClause("create_time");
        Page<SystemUser> page = PageHelper.startPage(pageNum, pageSize);
        List<SystemUser> suList = systemUserMapper.selectByExample(suExample);
        List<Map<String, Object>> list = new ArrayList<>();
        for (SystemUser su : suList) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", su.getId());
            map.put("username", su.getUsername());
            map.put("password", su.getPassword());
            map.put("employeeName", seMap.getOrDefault(su.getEmployeeId(), ""));
            map.put("roleName", srMap.getOrDefault(su.getRoleId(), ""));
            map.put("lastLoginTime", DateUtil.formatDateTime(su.getLastLoginTime()));
            map.put("createTime", DateUtil.formatDateTime(su.getCreateTime()));
            list.add(map);
        }
        return DataResult.build9100(new GridData(page.getTotal(), list));
    }

    @Override
    public DataResult insertUser(LoginInfo li, String username, String password) throws Exception {
        SystemUserExample suExample = new SystemUserExample();
        suExample.createCriteria().andIsDeleteEqualTo(0).andUsernameEqualTo(username);
        if (systemUserMapper.countByExample(suExample) > 0) {
            return DataResult.build9250("账号重复");
        }
        Date date = new Date();
        SystemUser newSu = new SystemUser();
        newSu.setUsername(username);
        newSu.setPassword(password);
        newSu.setEmployeeId(0L);
        newSu.setRoleId(0L);
        newSu.setLoginTime(date);
        newSu.setLastLoginTime(date);
        newSu.setIsDelete(0);
        newSu.setCreateTime(date);
        systemUserMapper.insert(newSu);
        return DataResult.build9100();
    }

    @Override
    public DataResult updateUser(LoginInfo li, long userId, String password) throws Exception {
        if (userId == 1L) {
            return DataResult.build9250("超级管理员无法修改或删除");
        }
        SystemUser newSu = new SystemUser();
        newSu.setId(userId);
        newSu.setPassword(password);
        systemUserMapper.updateByPrimaryKeySelective(newSu);
        return DataResult.build9100();
    }

    @Override
    public DataResult deleteUser(LoginInfo li, long userId) throws Exception {
        if (userId == 1L) {
            return DataResult.build9250("超级管理员无法修改或删除");
        }
        SystemUser newSu = new SystemUser();
        newSu.setId(userId);
        newSu.setIsDelete(1);
        systemUserMapper.updateByPrimaryKeySelective(newSu);
        return DataResult.build9100();
    }

    private String mixedEncryption(String str) throws Exception {
        return SecureUtil.md5(SecureUtil.sha1("Z" + SecureUtil.md5("X" + str) + "Y"));
    }

}
